package com.feinno.urcs.group.siptask;

import com.feinno.SDP.SessionDescription;
import com.feinno.SDP.SessionDescriptionParser;
import com.feinno.SDP.fields.AttributeField;
import com.feinno.cpim.CpimHeader;
import com.feinno.cpim.CpimMessage;
import com.feinno.msrp.message.MsrpRequest;
import com.feinno.sip.address.SipUri;
import com.feinno.sip.header.*;
import com.feinno.sip.header.ims.PCalledPartyID;
import com.feinno.sip.message.SipRequest;
import com.feinno.sip.provider.MessageFactoryImpl;
import com.feinno.threading.Future;
import com.feinno.threading.FutureListener;
import com.feinno.urcs.content.*;
import com.feinno.urcs.group.GroupConfiguration;
import com.feinno.urcs.group.GroupConstants;
import com.feinno.urcs.group.api.AndPcStatusService;
import com.feinno.urcs.group.api.GroupIndexService;
import com.feinno.urcs.group.api.GroupMessageLogService;
import com.feinno.urcs.group.api.GroupService;
import com.feinno.urcs.group.entity.GroupFileInfo;
import com.feinno.urcs.group.entity.GroupIndex;
import com.feinno.urcs.group.entity.GroupInfo;
import com.feinno.urcs.group.entity.GroupMessageType;
import com.feinno.urcs.group.log.MessageDirection;
import com.feinno.urcs.group.siptask.task.GroupFileTransferArgs;
import com.feinno.urcs.group.siptask.task.GroupTaskEvents;
import com.feinno.urcs.mobile.UserLocatorService;
import com.feinno.urcs.mobile.entity.UserType;
import com.feinno.urcs.nai.IMPU;
import com.feinno.urcs.profile.UserSessionService;
import com.feinno.urcs.profile.entity.ClientType;
import com.feinno.urcs.sip.SipConnector;
import com.feinno.urcs.sip.UrcsSipFactory;
import com.feinno.urcs.sipservlet.SipConnectorLoader;
import com.feinno.urcs.sipservlet.message.SipMessageBuilder;
import com.feinno.urcs.sipservlet.msrp.MsrpSessionFactory;
import com.feinno.urcs.sipservlet.uac.RcsFileTransferUAC;
import com.feinno.urcs.sipservlet.uac.RcsSipUACArgs;
import com.feinno.urcs.sipservlet.uac.RcsUACFactory;
import com.feinno.urcs.sipservlet.uac.RcsUACVisitorAdapter;
import com.feinno.urcs.sipservlet.utils.FileAttribute;
import com.feinno.urcs.storage.FileStream;
import com.feinno.urcs.storage.FileStreamService;
import com.feinno.urcs.types.MobileNo;
import com.feinno.util.DateUtil;
import com.feinno.util.Result;
import com.feinno.util.StringUtils;
import org.helium.framework.annotations.FieldSetter;
import org.helium.framework.annotations.ServiceSetter;
import org.helium.framework.annotations.TaskImplementation;
import org.helium.framework.task.Task;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.sip.header.DateHeader;
import javax.sip.header.Header;
import java.io.ByteArrayInputStream;
import java.nio.charset.Charset;
import java.text.ParseException;
import java.util.Calendar;
import java.util.UUID;

@TaskImplementation(event = GroupTaskEvents.GROUP_FILE_TRANSFER)
public class GroupFileTransferTask implements Task<GroupFileTransferArgs> {
	@FieldSetter("${CSCF_ADDRESS}")
	private String cscfAddress;
	@FieldSetter("${ISBC_ADDRESS}")
	private String isbcAddress;
	@FieldSetter("${AS_ADDRESS}")
	private String asAddress;
	@FieldSetter(loader = SipConnectorLoader.class, value = "${SIP_STACK_AS}")
	private SipConnector connector;
	@ServiceSetter
	private MsrpSessionFactory msrpFactory;
	@ServiceSetter
	private GroupIndexService groupIndexService;
	@ServiceSetter
	private GroupService groupService;
	@ServiceSetter
	private FileStreamService fileStreamService;
	@ServiceSetter
	private UserLocatorService userLocatorService;
	@ServiceSetter
	private UserSessionService userSessionService;
	@ServiceSetter
	private AndPcStatusService andPcStatusService;
	@ServiceSetter
	private GroupMessageLogService groupMessageLogService;
	private static final Logger LOGGER = LoggerFactory.getLogger(GroupFileTransferTask.class);

	@Override
	public void processTask(GroupFileTransferArgs args) {

		try {
			IMPU groupImpu = IMPU.parse(groupIndexService.getGroupById(args.getGroupId()).getConferenceUri());
			boolean isLocal = userLocatorService.lookup(args.getToMobile()).getType().equals(UserType.INNER);
			IMPU dest = IMPU.parse(args.getToMobile().toTelUri().toString());
			SipUri cscf = (SipUri) UrcsSipFactory.getAddressFactory().createURI(isLocal ? cscfAddress : isbcAddress);
			RcsSipUACArgs sipUACArgs = new RcsSipUACArgs(groupImpu, dest);
			FileAttribute fileAttribute = getFileAttribute(args.getGroupFileInfo());
			RcsFileTransferUAC uac = RcsUACFactory.createFileTransferUAC(connector, cscf, sipUACArgs,
					fileAttribute, msrpFactory.createSession());
			visitorUAC(uac, args, isLocal);
			LOGGER.info("begin send invite, from: {}, to: {}", groupImpu, dest);
			Future<Integer> future = uac.begin();    //send invite
			future.addListener(new FutureListener<Integer>() {
								   @Override
								   public void run(Result<Integer> result) {
									   try {
										   if (result.getValue() == null || result.getValue() < 200 || result.getValue() > 300) {
											   {
												   uac.end();
												   LOGGER.error(String.format("Send Group[%s] file failed. Member is %s. Error code is %s",
														   args.getGroupId(), dest, result.getValue()));
												   if (result.getError() != null) {
													   throw (Exception) result.getError();
												   }
											   }
										   }

//						if (result.getValue() != null && result.getValue() >= 200 && result.getValue() < 300) {

										   if (result.getValue() == 209) {
											   uac.end();
											   groupMessageLogService.fileMessageDownstreamLog(args.getGroupFileInfo().getTransferId(),
													   args.getFromMobile(), args.getToMobile().toTelUri().toString(),
													   isLocal ? "BF" : "NF",
													   groupIndexService.getGroupById(args.getGroupId()).getConferenceUri(),
													   "APP", MessageDirection.DOWN, args.getGroupFileInfo(), DateUtil.getUTCNow(),
													   cscf.toString(), "", result.getValue(), "209 "
											   );
											   return;
										   }
										   LOGGER.info("begin Send Group[{}] file. Member is {}", args.getGroupId(), dest);
										   FileStream fileStream = fileStreamService.get(args.getGroupFileInfo().getTransferId());
										   if (fileStream == null) {
											   uac.end();
											   groupMessageLogService.fileMessageDownstreamLog(args.getGroupFileInfo().getTransferId(),
													   args.getFromMobile(), args.getToMobile().toTelUri().toString(),
													   isLocal ? "BF" : "NF",
													   groupIndexService.getGroupById(args.getGroupId()).getConferenceUri(),
													   "APP", MessageDirection.DOWN, args.getGroupFileInfo(), DateUtil.getUTCNow(),
													   cscf.toString(), "", result.getValue(), "  fileStream is null");
											   throw new NullPointerException("fileStreamService.fileStream return null, id= "
													   + args.getGroupFileInfo().getTransferId());
										   }
										   Future<Integer> future = uac.send(fileStream.getInputStream());   //send msrp data packet
										   future.addListener(new FutureListener<Integer>() {
																  @Override
																  public void run(Result<Integer> result) {
																	  try {
																		  if (result.getValue() >= 200 && result.getValue() < 300) {
																			  LOGGER.info(String.format("Send Group[%s] msrp file ok. Member is %s. Error code is %s",
																					  args.getGroupId(), dest, result.getValue()));
																			  uac.end();
																			  groupMessageLogService.fileMessageDownstreamLog(args.getGroupFileInfo().getTransferId(),
																					  args.getFromMobile(), args.getToMobile().toTelUri().toString(),
																					  isLocal ? "BF" : "NF",
																					  groupIndexService.getGroupById(args.getGroupId()).getConferenceUri(),
																					  "APP", MessageDirection.DOWN, args.getGroupFileInfo(), DateUtil.getUTCNow(),
																					  cscf.toString(), "", result.getValue(), "OK");
																		  } else {
																			  uac.end();
																			  groupMessageLogService.fileMessageDownstreamLog(args.getGroupFileInfo().getTransferId(),
																					  args.getFromMobile(), args.getToMobile().toTelUri().toString(),
																					  isLocal ? "BF" : "NF",
																					  groupIndexService.getGroupById(args.getGroupId()).getConferenceUri(),
																					  "APP", MessageDirection.DOWN, args.getGroupFileInfo(), DateUtil.getUTCNow(),
																					  cscf.toString(), "", result.getValue(), "Fail");
																			  LOGGER.error(String.format("Send Group[%s] msrp file failed. Member is %s. Error code is %s",
																					  args.getGroupId(), dest, result.getValue()));
																			  if (result.getError() != null) {
																				  result.getError().printStackTrace();
																			  }
																		  }

																	  } catch (Exception e) {
																		  LOGGER.error(String.format("Send Group[%s]  msrp file failed. Member is %s. Error code is %s",
																				  args.getGroupId(), dest, result.getValue()));
																	  }
																  }
															  }
										   );
									   } catch (Exception ex) {
										   LOGGER.error(String.format("Send Group[%s] file failed. Member is %s. Error code is %s",
												   args.getGroupId(), dest, result.getValue()));
									   }
								   }
							   }

			);
		} catch (Exception ex) {
			LOGGER.warn("Send Group[{}] file except. Member is {}. Error is {}", args.getGroupId(), args.getToMobile(), ex.getMessage());
		}
	}

	private FileAttribute getFileAttribute(GroupFileInfo groupFileInfo) {
		FileAttribute fileAttribute = new FileAttribute();
		fileAttribute.setFileName(groupFileInfo.getFileName());
		fileAttribute.setFileType(groupFileInfo.getFileType());
		fileAttribute.setFileSize(groupFileInfo.getFileSize());
		fileAttribute.setSend(groupFileInfo.isSendonly());
		fileAttribute.setRecv(groupFileInfo.isRecvonly());
		fileAttribute.setAcceptType(groupFileInfo.getAcceptType());
		fileAttribute.setAcceptWrappedType(groupFileInfo.getAcceptWrappedType());
		fileAttribute.setTransferId(groupFileInfo.getTransferId());
		return fileAttribute;
	}

	private void visitorUAC(RcsFileTransferUAC uac, GroupFileTransferArgs args, boolean isLocal) {
		uac.accept(new RcsUACVisitorAdapter() {
			@Override
			public void visitorSend(SipRequest request) {
				super.visitorSend(request);
				try {
					GroupIndex groupIndex = groupIndexService.getGroupById(args.getGroupId());
					GroupInfo groupInfo = groupService.getGroupInfo(groupIndex);
					String groupUri = groupIndex.getConferenceUri();
					IMPU groupImpu = IMPU.parse(groupUri);
					request.getContactHeader().setAddress(UrcsSipFactory.getAddressFactory().createAddress(groupImpu.toUri()));
					SipMessageBuilder smb = new SipMessageBuilder(request);
					request.removeHeader(Contact.CONTACT);
					smb.recordRoute(UrcsSipFactory.getAddressFactory().createAddress(asAddress))
							.supported("100rel,histinfo,timer,209file")
							.allow("INVITE,ACK,BYE,CANCEL,PRACK,UPDATE,OPTIONS,REGISTER,INFO,MESSAGE,SUBSCRIBE,NOTIFY,REFER,PUBLISH")
							.acceptContact(GroupConstants.PREFIX_ACCEPT_CONTACT + GroupConstants.G_3GPP_ICSI_REF_FILETRANSFER_KEY_VALUE)
							.subject(groupInfo.getSubject() != null ? groupInfo.getSubject() : "")
							.contact(GroupConfiguration.INSTANCE.getGroupPrefix() + args.getGroupId(), groupInfo.getDomain(),
									GroupConstants.G_3GPP_ICSI_REF_FILETRANSFER_KEY_VALUE, true)
							.conversationId(groupInfo.getConversationId())
							.contributionId(UUID.randomUUID().toString())
							.referBy(args.getFromMobile().toAddress());
					if (args.getGroupFileInfo().getMessageType() == GroupMessageType.IMG) {
						smb.pas(GroupConstants.HEADER_PAS_PPS_IMAGE);
					} else {
						smb.pas(GroupConstants.HEADER_PAS_PPS_FILETRANSFER);
					}
					request = smb.toSipReqeust();
					PCalledPartyID pcpi = new PCalledPartyID();
					pcpi.setAddress(args.getToMobile().toAddress());
					request.addHeader(pcpi);
					Header hotFileHeader = MessageFactoryImpl.getInstance().getHeaderFactory().createHeader("Hot-File",
							args.getGroupFileInfo().getFileId() == null ? args.getGroupFileInfo().getTransferId() : args.getGroupFileInfo().getFileId());
					request.setHeader(hotFileHeader);
					DateHeader dateHeader = new SIPDateHeader();
					Calendar instance = Calendar.getInstance();
					instance.setTime(args.getSendTime());
					dateHeader.setDate(instance);
					request.addHeader(dateHeader);
					Cpim cpimMessage = createCpimMessage(args, isLocal);
					UrcsMultipartMimeContent content = UrcsMultipartMimeContent.newInstance(request.getContentTypeHeader(), request.getRawContent());
					if (args.getGroupFileInfo().getThumbnail() != null) {
						//add thumbnail
						String contentId = UUID.randomUUID().toString();
						SIPHeader contentID = (SIPHeader) MessageFactoryImpl.getInstance().getHeaderFactory().createHeader("Content-ID",
								String.format("<%s>", contentId));
						ContentDisposition contentDisposition = (ContentDisposition) MessageFactoryImpl.getInstance().
								getHeaderFactory().createContentDispositionHeader("icon");
						SIPHeader contentLength = (SIPHeader) MessageFactoryImpl.getInstance().getHeaderFactory().
								createHeader("Content-Length", args.getGroupFileInfo().getThumbnail().length + "");
						UrcsContent imageContent;
						// 依据args 中传递的 Content-Transfer-Encoding 判断是否需要添加这个头, 因为这个头影响到内容的编码.
						String contentTransferEncoding = args.getGroupFileInfo().getContentTransferEncoding();
						if (contentTransferEncoding != null && contentTransferEncoding.trim().length() > 0) {
							SIPHeader contentTransferEncodingHeader = (SIPHeader) MessageFactoryImpl.getInstance().getHeaderFactory().
									createHeader(GroupConstants.HEADER_CONTENT_TRANSFER_ENCODING, contentTransferEncoding); //binary
							imageContent = (UrcsContent) UrcsContentFactory.newContent(args.getGroupFileInfo().getThumbnail(),
									Charset.defaultCharset(),
									MimeType.ImageJpeg,
									contentID,
									contentDisposition,
									contentLength
									, contentTransferEncodingHeader
							);
						} else {
							imageContent = (UrcsContent) UrcsContentFactory.newContent(args.getGroupFileInfo().getThumbnail(),
									Charset.defaultCharset(),
									MimeType.ImageJpeg,
									contentID,
									contentDisposition,
									contentLength
							);
						}
						content.addContent(imageContent);
					}

					if (args.getGroupFileInfo().getTimelen() != 0) {
						//add play timelen for image and video
						Sdp sdp = content.getContent(Sdp.class);
						SessionDescription sessionDescription = SessionDescriptionParser.parse(sdp.getContentString());
						sessionDescription.getMediaDescription("message").addUnknownField(new AttributeField("file-disposition",
								String.format("timelen=%s", args.getGroupFileInfo().getTimelen())));
						sdp.setContent(sessionDescription.toString().getBytes("utf-8"));
					}

					if (args.getGroupFileInfo().getMessageType() == GroupMessageType.LOCATION) {
						Sdp sdp = content.getContent(Sdp.class);
						SessionDescription sessionDescription = SessionDescriptionParser.parse(sdp.getContentString());
						sessionDescription.getMediaDescription("message").addUnknownField(new AttributeField("accept-types",
								args.getGroupFileInfo().getAcceptType()));
						if (!StringUtils.isNullOrEmpty(args.getGroupFileInfo().getAcceptWrappedType())) {
							sessionDescription.getMediaDescription("message").addUnknownField(new AttributeField("accept-wrapped-types",
									args.getGroupFileInfo().getAcceptWrappedType()));
						}
						sdp.setContent(sessionDescription.toString().getBytes("utf-8"));
					}
					content.addContent(cpimMessage);
					request.setContent(content.toByteArray(), content.getContentTypeHeader());
				} catch (Exception ex) {
					LOGGER.warn(String.format("visitorSend Group[%s] file except. Member is %s.  ", args.getGroupId(), args.getToMobile()), ex);
				}
			}

			@Override
			public void visitorSend(MsrpRequest request) {
				if (args.getGroupFileInfo().getMessageType() == GroupMessageType.CARD) {
					request.setContentType("text/vcard");
				} else if (args.getGroupFileInfo().getMessageType() == GroupMessageType.LOCATION) {
					request.setContentType("application/vnd.gsma.rcspushlocation+xml");
				} else if (args.getGroupFileInfo().getMessageType() == GroupMessageType.OUTCE || args.getGroupFileInfo().getMessageType() == GroupMessageType.FILE) {
					request.setContentType("message/CPIM");
				} else {
					request.setContentType("application/octet-stream");
				}
			}
		});
	}

	public Cpim createCpimMessage(GroupFileTransferArgs sendFileArgs, boolean isLocal) throws ParseException {
		CpimMessage cpimMessage = new CpimMessage(true);
		cpimMessage.addHeader(new CpimHeader(CpimHeader.FROM, sendFileArgs.getFromMobile().toTelUri().toString()));
		cpimMessage.addHeader(new CpimHeader(CpimHeader.TO, sendFileArgs.getToMobile().toTelUri().toString()));
		cpimMessage.addHeader(new CpimHeader(CpimHeader.MESSAGE_ID, UUID.randomUUID().toString()));
		// 静默标记(静默消息NS)
		addSlienceSupport(cpimMessage, isLocal, sendFileArgs.getToMobile());
		cpimMessage.addHeader(new CpimHeader(CpimHeader.NS, "CMCCfeature<urn: CMCCFeatures@rcs.chinamobile.com>"));
		cpimMessage.addHeader(new CpimHeader(CpimHeader.DATETIME, cpimMessage.getUTCTimeNow()));
		if (sendFileArgs.getGroupFileInfo().getStatusReport() != null) {
			ContentType contentTypeHeader = (ContentType) MessageFactoryImpl.getInstance().getHeaderFactory().createContentTypeHeader("message", "cpim");
			Cpim cpim = (Cpim) UrcsContentFactory.newContent(sendFileArgs.getGroupFileInfo().getStatusReport(),
					Charset.defaultCharset(),
					contentTypeHeader);
			if (cpim.getCpimMessage().getHeader(CpimHeader.Disposition_Notification) != null) {
				cpimMessage.addHeader(new CpimHeader(CpimHeader.Disposition_Notification,
						cpim.getCpimMessage().getHeader(CpimHeader.Disposition_Notification).getValue()));
			}
		}
		cpimMessage.addHeader(new CpimHeader(CpimHeader.CONTENT_TYPE, MimeType.CPIM.getValue()));
		cpimMessage.addHeader(new CpimHeader(CpimHeader.CONTENT_LENGTH, String.valueOf(0)));
		ContentType contentTypeHeader = (ContentType) MessageFactoryImpl.getInstance().getHeaderFactory().createContentTypeHeader("message", "cpim");
		Cpim cpim = (Cpim) UrcsContentFactory.newContent(cpimMessage.getByteArray(),
				Charset.defaultCharset(), contentTypeHeader);
		return cpim;
	}

	private void addSlienceSupport(CpimMessage cpimMessage, boolean isLocal, MobileNo toMobile) {
		CpimHeader silenceSupportedHeader = null;
		if (isLocal) {
			LOGGER.info(String.format("Mobile=%s is INNER", toMobile));
			ClientType activeClient = userSessionService.getActiveClient(toMobile);
			if (null != activeClient && activeClient == ClientType.PC) {
				silenceSupportedHeader = new CpimHeader("CMCCfeature.silenceSupported", "true");
			} else {
				silenceSupportedHeader = new CpimHeader("CMCCfeature.silenceSupported", "false");
			}
		} else {
			try {
				LOGGER.info(String.format("CMCCfeature.silenceSupported  Mobile=%s is OUTER", toMobile));
				int avitveType = andPcStatusService.getUserMessageActive(toMobile);
				LOGGER.info(String.format("CMCCfeature.silenceSupported Mobile=%s  avitveType=%s", toMobile, avitveType));

				if (avitveType == 1) {
					silenceSupportedHeader = new CpimHeader("CMCCfeature.silenceSupported", "true");
				} else {
					silenceSupportedHeader = new CpimHeader("CMCCfeature.silenceSupported", "false");
				}
			} catch (Exception e) {
				LOGGER.error(String.format("get PC status error Mobile=%s ", toMobile), e);
				silenceSupportedHeader = new CpimHeader("CMCCfeature.silenceSupported", "false");
			}
		}
		cpimMessage.addHeader(silenceSupportedHeader);
	}
}